package com.atguigu2.statement.crud;

import com.atguigu.bean.User;
import com.atguigu.util.JDBCUtils;
import org.junit.Test;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

// 使用PreparedStatement执行curd语句
public class PreparedStatementQueryTest {
  @Test
  public void testLogin() {
    String user = "AA", password = "123456";
    /*
    Statement 执行方式：会遭到sql注入；如username = "1 or 1"--> where user = 1 or 1 全为真
     String sql = "SELECT user,password FROM user_table WHERE user = " + username;
     Statement st = conn.createStatement();
     ResultSet rs = st.executeQuery(sql);
    */
    // PreparedStatement占位符, user是数据库的字段名，但username才是User实体属性名，需要使用别名转换
    String sql = "SELECT user as username,password FROM user_table WHERE user = ? and password = ?";
    User returnUser = getInstance(User.class, sql, user, password);
    System.out.println(returnUser);

    // 增删改操作见下。。。没啥好说的
    // String sql = "update customers set name = ? where id = ?";
    // prepareStatement   ps.execute(); 返回true/false
    // ps.executeUpdate(); 返回函数

  }

  // 用来执行查询语句，并且该语句只返回一行数组的那种
  // 通过使用反射 + 泛型能给多个表通用
  // 比如查询用户 getInstance(User.class,"select * from user where id = ?", 1)
  // 查询商品 getInstance(Goods.class,"....where name = ? and price = ?", "shoes",1)
  public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
      // atiguigu3/util/JDBCUtils一个封装的工具类，用来获取-结束connection连接，不需要你关心
      conn = JDBCUtils.getConnection();
      ps = conn.prepareStatement(sql);
      for (int i = 0; i < args.length; i++) {
        // 给占位符？们填充真实数据, 其实有setXXX很多方法，但setObject通用
        ps.setObject(i + 1, args[i]);
      }
      rs = ps.executeQuery();
      // 获取结果集的元数据 :ResultSetMetaData
      ResultSetMetaData rsmd = rs.getMetaData();
      // 通过ResultSetMetaData获取结果集中的列数
      int columnCount = rsmd.getColumnCount();
      // 如果有多行--可以 while(rs.next())
      if (rs.next()) { // 与和iterator迭代器原理类似，每次一行
        T t = clazz.newInstance(); // 生成空属性值对象
        // 遍历一行中的每一列
        for (int i = 0; i < columnCount; i++) {
          // 获取列值,其实有 getXXX多种方法，为了通用必须Object
          Object columValue = rs.getObject(i + 1);
          // 获取每个列的列名
          // String columnName = rsmd.getColumnName(i + 1); //数据库列名
          String columnLabel = rsmd.getColumnLabel(i + 1); // 自定义的as别名 === 实体属性名称
          // 给t对象指定的columnName属性，赋值为columValue：通过反射
          Field field = clazz.getDeclaredField(columnLabel);
          field.setAccessible(true); // 访问private属性
          field.set(t, columValue); // 相当于 T.xx = value
        }
        return t;
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      JDBCUtils.closeResource(conn, ps, rs);
    }
    return null;
  }
}
